/*
 * This file is part of ELCube.
 *
 * ELCube is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ELCube is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ELCube.  If not, see <https://www.gnu.org/licenses/>.
 */
package cn.nkpro.elcube.co.spel;

import cn.nkpro.elcube.co.http.NkHttpResponse;
import cn.nkpro.elcube.data.redis.RedisSupport;
import cn.nkpro.elcube.exception.NkDefineException;
import cn.nkpro.elcube.exception.NkHttpException;
import cn.nkpro.elcube.platform.service.PlatformRegistryService;
import cn.nkpro.elcube.security.SecurityUtilz;
import cn.nkpro.elcube.security.UserBusinessAdapter;
import cn.nkpro.elcube.security.bo.UserDetails;
import cn.nkpro.elcube.security.validate2.AuthenticationUtils;
import cn.nkpro.elcube.utils.DateTimeUtilz;
import cn.nkpro.elcube.utils.UUIDHexGenerator;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component("SpELnk")
public class NkDefaultSpEL extends NkAbstractHttpSpELInjection {

    @Autowired@SuppressWarnings("all")
    private PlatformRegistryService registryService;
    @Autowired@SuppressWarnings("all")
    private UserBusinessAdapter userBusinessAdapter;

    /**
     * 推荐使用 @dict.json()
     * @param key key
     * @return json
     */
    @SuppressWarnings("unused")
    @Deprecated
    public Object dict(String key){
        return registryService.getJSON("@DICT",key);
    }

    @SuppressWarnings("unused")
    public String account(){
        return SecurityUtilz.getUser().getObjectId();
    }

    @SuppressWarnings("unused")
    public String realname(){
        return SecurityUtilz.getUser().getRealname();
    }

    @SuppressWarnings("unused")
    public UserDetails user(){
        return SecurityUtilz.getUser();
    }

    @SuppressWarnings("unused")
    public Object me(){
        return userBusinessAdapter.getUser(SecurityUtilz.getUser());
    }


    /// 调用ELCube自己


    @Value("${nk.pipeline.url:}")
    private String apiUrl;

    @Value("${nk.pipeline.key:}")
    private String key;

    @Value("${nk.pipeline.secretKey:}")
    private String secretKey;

    @Value("${nk.pipeline.expire:86400}")
    private Long expire = 60*60*24L;

    @Autowired
    private RedisSupport<JSONObject> redis;

    @Autowired
    AuthenticationUtils authenticationUtils;

    private Pattern pattern = Pattern.compile("([/\\w]+)(\\?(\\w+(=[^&=%?]*)?([&]\\w+(=[^&=%?]*)?)*))?");
    private Map<String,String> getHeader(String url){

        Map<String,String> headers = new HashMap<>();

        Matcher matcher = pattern.matcher(url);
        if(matcher.matches()){
            String requestUrl = matcher.group(1);
            String queryString = matcher.group(3);

            Map<String, String> parameterMap = Arrays.stream(StringUtils.split(queryString, "&"))
                    .map(item -> StringUtils.split(item, "="))
                    .collect(Collectors.toMap(
                            e -> e[0],
                            e -> e.length>=2?e[1]:StringUtils.EMPTY
                    ));

            String nonce = UUIDHexGenerator.nonce();
            String timestamp = String.valueOf(DateTimeUtilz.nowSeconds());

            headers.put("elcube-nonce", nonce);
            headers.put("elcube-timestamp",timestamp);
            headers.put("elcube-appKey",key);
            headers.put("elcube-signature",AuthenticationUtils.signature(
                    requestUrl,
                    parameterMap,
                    timestamp,
                    secretKey,
                    nonce
            ));

            return headers;

        }else{
            throw new NkDefineException("Pipeline 表达式url不合法");
        }
    }

    public Object pipeline(String url) {

        Assert.isTrue(StringUtils.isNotBlank(apiUrl),"请配置pipeline服务地址，如-Dnk.pipeline.url=http://rule.elcube.it/api");
        Assert.isTrue(StringUtils.isNotBlank(key),"请配置pipeline服务地址，如-Dnk.pipeline.key=***");
        Assert.isTrue(StringUtils.isNotBlank(secretKey),"请配置pipeline服务地址，如-Dnk.pipeline.secretKey=****");


        if(StringUtils.isNotBlank(apiUrl)){
            return redis.getIfAbsent(hashCode(url,null,null),expire,true,()->{
                String requestUrl = "/pipeline/" +url;
                NkHttpResponse response = doGet(apiUrl + requestUrl, getHeader(requestUrl));
                if(response.getStatusCode()==200){
                    return JSON.parseObject(response.getBody());
                }
                try{
                    JSONObject jsonObject = JSON.parseObject(response.getBody());
                    throw new NkHttpException("远程服务调用错误："+jsonObject.getString("msg"));
                }catch (Exception e){
                    throw new NkHttpException("远程服务调用错误："+response.getBody());
                }
            });
        }
        throw new NkHttpException("ELCube Pipeline EndPoint 尚未配置");
    }
}
